-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

-- Description: 管理策略重配的标准文件
local class = require 'mc.class'
local json = require 'cjson'
local log = require 'mc.logging'
local policy_ipmi_format = require 'domain.policy_config.policy_ipmi_format'
local base_messages = require 'messages.base'
local custom_messages = require 'messages.custom'
local bios_factory = require 'factory.bios_factory'

local PolicyRegistery = class()

-- 1、解析registery文件
-- 2、支持校验属性是否合法、属性值是否合法
-- 3、支持将属性转换为ipmi格式命令
function PolicyRegistery:ctor(system_id)
    self.version = ''
    self.header = {}
    self.attributes_json_obj = {}
    self.functions_json_obj = {}
    self.system_id = system_id
end

function PolicyRegistery:init()
    self:build_registery()
end

-- 1、解析registery文件
function PolicyRegistery:build_registery()
    local bios_ser = bios_factory.get_service('bios_service')
    local registery_data = bios_ser:get_config_data('PolicyRegisteryJson', self.system_id)
    if not registery_data then
        log:error('[bios]policy registery: registery data invalid')
        error({Code = base_messages.ActionNotSupported('policy config')})
    end
    local ok, registery_json_obj = pcall(json.json_object_ordered_decode, registery_data)
    if not ok then
        log:error('[bios]policy registery: decode registery data fail')
        error({Code = base_messages.ActionNotSupported('policy config')})
    end
    if not registery_json_obj or not registery_json_obj.Header or
        not registery_json_obj.Registries or not registery_json_obj.Registries.Attributes or
        not registery_json_obj.FunctionDetails or not registery_json_obj.FunctionDetails.FunctionInfo then
        log:error('[bios]policy registery: registery data format invalid')
        error({Code = base_messages.MalformedJSON()})
    end
    self.header = registery_json_obj.Header
    self.attributes_json_obj = registery_json_obj.Registries.Attributes
    self.functions_json_obj = registery_json_obj.FunctionDetails
end

function PolicyRegistery:is_enum_valid(attribute, value)
    local value_list = attribute.Value
    if not value_list then
        log:error('[bios]policy registery: attribute(%s) has not value list', attribute.AttributeName)
        error({Code = base_messages.MalformedJSON()})
    end
    for _, v in pairs(value_list) do
        if value == v.ValueName then
            return
        end
    end
    log:error('[bios]policy registery: attribute(%s) set invalid value(%s)', attribute.AttributeName, value)
    error({Code = base_messages.PropertyValueNotInList(value, 'Attributes/' .. attribute.AttributeName)})
end

function PolicyRegistery:is_integer_valid(attribute, value)
    if type(value) ~= 'number' then
        log:error('[bios]policy registery: attribute(%s) set invalid type value(%s)',
            attribute.AttributeName, value)
        error({Code = base_messages.PropertyValueTypeError(value, 'Attributes/' .. attribute.AttributeName)})
    end
    if not attribute.LowerBound or not attribute.UpperBound then
        log:error('[bios]policy registery: attribute(%s) has not bound', attribute.AttributeName)
        error({Code = base_messages.MalformedJSON()})
    end
    if value < attribute.LowerBound or value > attribute.UpperBound then
        log:error('[bios]policy registery: attribute(%s) set value(%s) not range from %s to %s',
            attribute.AttributeName, value, attribute.LowerBound, attribute.UpperBound)
        error({Code = custom_messages.ValueOutOfRange('Attributes/' .. attribute.AttributeName)})
    end
end

-- 2、支持批量校验属性
function PolicyRegistery:validate(config_data)
    local attribute_valid_fun = {
        Enumeration = PolicyRegistery.is_enum_valid,
        Integer = PolicyRegistery.is_integer_valid
    }
    for k, v in pairs(config_data) do
        local attribute = self.attributes_json_obj[k]
        if not attribute then
            log:error('[bios]policy registery: invalid attribute(%s)', k)
            error({Code = base_messages.PropertyUnknown('Attributes/' .. k)})
        end
        local valid_fun = attribute_valid_fun[attribute.Type]
        if not valid_fun then
            log:error('[bios]policy registery: attribute(%s) get valid fun fail', k)
            error({Code = base_messages.InternalError()})
        end
        valid_fun(self, attribute, v)
    end
end

function PolicyRegistery:get_function_id(prop)
    local function_id = self.attributes_json_obj[prop].FunctionId
    if not function_id then
        log:error('[bios]policy registery: get attribute(%s) function id fail', prop)
        error({Code = base_messages.PropertyUnknown('Attributes/' .. prop)})
    end
    return function_id
end

function PolicyRegistery:get_function(function_id)
    local function_val = self.functions_json_obj.FunctionInfo[function_id]
    if not function_val then
        log:error('[bios]policy registery: get function id(%s) function fail', function_id)
        error({Code = base_messages.MalformedJSON()})
    end
    return function_val
end

-- 3、支持将属性转换为ipmi命令
function PolicyRegistery:build_ipmi(config_data)
    self:validate(config_data)
    local ipmi_format_tbl = {}
    local function_id
    local ipmi_format
    for attribute_name, _ in pairs(config_data) do
        function_id = self:get_function_id(attribute_name)
        if not ipmi_format_tbl[function_id] then
            ipmi_format = policy_ipmi_format.new()
            ipmi_format:parse(self:get_function(function_id))
            ipmi_format_tbl[function_id] = ipmi_format
        end
    end
    return ipmi_format_tbl
end

return PolicyRegistery